package cheme.ProcessEditor;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.draw2d.FreeformViewport;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.KeyHandler;
import org.eclipse.gef.KeyStroke;
import org.eclipse.gef.MouseWheelHandler;
import org.eclipse.gef.MouseWheelZoomHandler;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.SnapToGeometry;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.editparts.ScalableRootEditPart;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.DeleteAction;
import org.eclipse.gef.ui.actions.DirectEditAction;
import org.eclipse.gef.ui.actions.GEFActionConstants;
import org.eclipse.gef.ui.actions.SaveAction;
import org.eclipse.gef.ui.actions.SelectionAction;
import org.eclipse.gef.ui.actions.ToggleSnapToGeometryAction;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.palette.PaletteViewerProvider;
import org.eclipse.gef.ui.palette.FlyoutPaletteComposite.FlyoutPreferences;
import org.eclipse.gef.ui.parts.GraphicalEditorWithFlyoutPalette;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.gef.ui.properties.UndoablePropertySheetEntry;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.PropertySheetPage;

import cheme.ProcessEditor.Actions.ProcessContextMenuProvider;
import cheme.ProcessEditor.DirectEdit.StatusLineValidationMessageHandler;
import cheme.ProcessEditor.DnD.DataEditDropTargetListener;
import cheme.ProcessEditor.Parts.Factory.EditpartFactory;
import cheme.ProcessEditor.Views.ChemeOverviewOutline;
import cheme.ProcessModel.ConsoleViewModel;
import cheme.ProcessModel.ContentsModel;
import cheme.ProcessModel.IChild;
import cheme.ProcessModel.ModelEnv;
import cheme.ProcessModel.ProcessModel;


public class ProcessEditor
		extends GraphicalEditorWithFlyoutPalette implements
		CommandStackListener, ISelectionListener
{
	public static final String ID = "cheme.ProcessEditor.editor";

	ProcessModel processModel = null;

	/** the undoable <code>IPropertySheetPage</code> */
	private PropertySheetPage undoablePropertySheetPage;

	/** the overview outline page */
	private OverviewOutlinePage overviewOutlinePage;
	
	private boolean editorSaving = false;
	
	protected static final String PALETTE_DOCK_LOCATION = "Dock location"; //$NON-NLS-1$

	protected static final String PALETTE_SIZE = "Palette Size"; //$NON-NLS-1$

	protected static final String PALETTE_STATE = "Palette state"; //$NON-NLS-1$

	protected static final int DEFAULT_PALETTE_SIZE = 130;	
	

	static
	{
		Activator.getDefault().getPreferenceStore().setDefault(PALETTE_SIZE, DEFAULT_PALETTE_SIZE);
	}
	
	private IPartListener partListener = new IPartListener()
	{
		public void partOpened(IWorkbenchPart part)
		{
		}
		
		public void partClosed(IWorkbenchPart part)
		{
			System.out.print("closing");
		}
		
		// If an open, unsaved file was deleted, query the user to either do a "Save As"
		// or close the editor.
		public void partActivated(IWorkbenchPart part)
		{
			if (part != ProcessEditor.this)
				return;
			if (!((IFileEditorInput)getEditorInput()).getFile().exists()) 
			{
				Shell shell = getSite().getShell();
				String title = "Confirm Delete";
				String message = "This file has not been saved. Would you like to save it now before deleting it?";
				String[] buttons = {"Save", "Close"};
				
				MessageDialog dialog = new MessageDialog(shell, title, null, message, MessageDialog.QUESTION, buttons, 0);
				
				if (dialog.open() == 0)
				{
					//TODO:
					if (!performSaveAs())
					{
						partActivated(part);
					}
				} 
				else
				{
					closeEditor(false);
				}
			}
		}

		public void partDeactivated(IWorkbenchPart part)
		{
		}

		public void partBroughtToTop(IWorkbenchPart part)
		{
		}

		
	};

//	 This class listens to changes to the file system in the workspace, and
	// makes changes accordingly.
	// 1) An open, saved file gets deleted -> close the editor
	// 2) An open file gets renamed or moved -> change the editor's input
	// accordingly
	class ResourceTracker implements IResourceChangeListener, IResourceDeltaVisitor
	{
		public void resourceChanged(IResourceChangeEvent event)
		{
			IResourceDelta delta = event.getDelta();
			try
			{
				if(delta != null)
				{
					delta.accept(this);
				}
			}
			catch(CoreException exception)
			{
				// What should be done here?
			}
		}

		public boolean visit(IResourceDelta delta)
		{
			if(delta == null || !delta.getResource().equals(((IFileEditorInput)getEditorInput()).getFile()))
			{
				return true;
			}

			if(delta.getKind() == IResourceDelta.REMOVED)
			{
				Display display = getSite().getShell().getDisplay();
				
				if((IResourceDelta.MOVED_TO & delta.getFlags()) == 0)
				{ 
					/*
					 * if the file was deleted 
					 * NOTE: The case where an open, unsaved file is deleted is 
					 * being handled by the PartListener added to the Workbench 
					 * in the initialize() method.
					 */
					
					display.asyncExec(new Runnable()
					{
						public void run()
						{
							if(!isDirty())
							{
								closeEditor(false);
							}
						}
					});
				}
				else
				{ 
					/*
					 * else if it was moved or renamed
					 */
					final IFile newFile = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getMovedToPath());
					display.asyncExec(new Runnable()
					{
						public void run()
						{
							superSetInput(new FileEditorInput(newFile));
						}
					});
				}
			}
			else if(delta.getKind() == IResourceDelta.CHANGED)
			{
				if(!editorSaving)
				{
					// the file was overwritten somehow (could have been
					// replaced by another
					// version in the respository)
					final IFile newFile = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getFullPath());
					Display display = getSite().getShell().getDisplay();
					display.asyncExec(new Runnable()
					{
						public void run()
						{
							setInput(new FileEditorInput(newFile));
							getCommandStack().flush();
						}
					});
				}
			}
			return false;
		}
	}
	
	private ResourceTracker resourceListener = new ResourceTracker();
	
	/**
	 * No-arg constructor
	 */
	public ProcessEditor()
	{
		/*----------------------------------------------------------------------
		 * EditDomain
		 * An EditDomain is an interface that logically bundles an editor, 
		 * viewers, and tools. Therefore, it defines the real editor application.
		 * 
		 * An EditDomain provides a CommandStack, which keeps track of all 
		 * executed commands. This is necessary for undo and redo operations and 
		 * useful to determine if the model was modified (is dirty) or not.
		 * 
		 * Usually you will have one EditDomain per editor, but it is also 
		 * possible to share an EditDomain across several editors in a 
		 * multi-page editor.
		 * 
		 * It is up to you when to create the EditDomain. It is possible to 
		 * create it lazily. You can use the class EditDomain directly, however, 
		 * the Graphical Editing Framework provides an implementation, which 
		 * additionally knows about the editor that created it. This 
		 * implementation is called DefaultEditDomain.
		 */
		setEditDomain(new ChemeEditDomain(this));		
	}

	/**
	 * Initializes the editor.
	 */
	@Override
	public void init(IEditorSite site, IEditorInput input) throws PartInitException
	{
		super.init(site, input);
	}
	
	/**
	 * Creates a new <code>GraphicalViewer</code>, configures, registers and
	 * initializes it.
	 * 
	 * @param parent the parent composite
	 * @return a new <code>GraphicalViewer</code>
	 */
	@Override
	protected void createGraphicalViewer(Composite parent)
	{
		StatusLineValidationMessageHandler validationMessageHandler = new StatusLineValidationMessageHandler(getEditorSite());
		GraphicalViewer viewer = new ValidationEnabledGraphicalViewer(validationMessageHandler);

		viewer.createControl(parent);
		setGraphicalViewer(viewer);
		configureGraphicalViewer();
		hookGraphicalViewer();
		initializeGraphicalViewer();
		
//		// hook the viewer into the EditDomain
//		// ???? getEditDomain().addViewer(viewer);
//
//		// acticate the viewer as selection provider for Eclipse
//		// ???? getSite().setSelectionProvider(viewer);
	}
	
	protected void configureGraphicalViewer()
	{
		super.configureGraphicalViewer();

		GraphicalViewer viewer = getGraphicalViewer();
		
		viewer.setEditPartFactory(new EditpartFactory());
		
//		ScalableFreeformRootEditPart rep = new ScalableFreeformRootEditPart();
//		viewer.setRootEditPart(rep);
		
		ChemeRootEditPart rootEditPart = new ChemeRootEditPart(); 
		viewer.setRootEditPart(rootEditPart);
	
		
		/*
		 * I tried to add a A selection listener that would reorder/bring to the top
		 * the elements that were most recently selected. nfortunately this does not work well
		 * Part of the problem here is that the z order of figures is closely tied to the 
		 * order of parts AND the order of model elements. The latter cannot be trivially removed 
		 * and added to the model if they are connected to other processors. I have no good 
		 * solution as of 10-20-2007
		 */
//		viewer.addSelectionChangedListener(new ISelectionChangedListener()
//		{
//
//			public void selectionChanged(SelectionChangedEvent event)
//			{
//				if(event.getSelection() instanceof StructuredSelection)
//				{
//					Iterator itr = ((StructuredSelection)event.getSelection()).iterator();
//					GraphicalEditPart current = null;
//					
//					//get each element/part/figure (depending on the level you try to manage) of the new selection
//					while(itr.hasNext())
//					{
//						//Do remove it from the list and add it to the back/top
//						current = (GraphicalEditPart)itr.next();
//						
//						if(current.getModel() instanceof IChild)
//						{
//							IChild child = (IChild)current.getModel();
//							
//							child.getParent().remove(child);
//							child.getParent().add(child);
//						}
//					}
//				}
//			}
//		});
		
		/*----------------------------------------------------------------------
		 * Zooming!
		 * Note that, since the zooming requires a valid zoom manager, which is
		 * associated with a root edit part, and that root edit part is not 
		 * created until this function is called, the installation of zoom actions
		 * is delayed till now rather than handled in the framework called 
		 * createActions().
		 */
		ZoomManager manager = ((ScalableFreeformRootEditPart)viewer.getRootEditPart()).getZoomManager();//rootEditPart.getZoomManager();
		setupZoomContributors(manager);
		setupZoomProperties(manager);
		setupZoomActions(manager);
		
		/*----------------------------------------------------------------------
		 * Snapping to geometry!
		 * Note that, since the snap to geometry bit needs a valid graphical 
		 * viewer, the associated actions are set up here by calling 
		 * setupSnapToGeometryActions() rather than in the framework called 
		 * createActions().
		 */
		setupSnapToGeometryProperties();
		setupSnapToGeometryActions();
		
		
		
		viewer.addDropTargetListener(new DataEditDropTargetListener(viewer));
		
		ContextMenuProvider provider = new ProcessContextMenuProvider(viewer, getActionRegistry());
		viewer.setContextMenu(provider);
		getSite().registerContextMenu("cheme.ProcessEditor", provider, viewer);
		
		// initialize actions
		createActions();
		
		/*
		 * Just handling key input to the editor.
		 */
		KeyHandler keyHandler = new KeyHandler();
		keyHandler.put(KeyStroke.getPressed(SWT.DEL, 127, 0), getActionRegistry().getAction(ActionFactory.DELETE.getId()));
		
		keyHandler.put(KeyStroke.getPressed('z', SWT.CTRL), getActionRegistry().getAction(ActionFactory.UNDO.getId()));
		
		keyHandler.put(KeyStroke.getPressed(' ', 0), getActionRegistry().getAction(GEFActionConstants.DIRECT_EDIT));
		keyHandler.put(KeyStroke.getPressed(SWT.F2, 0), getActionRegistry().getAction(GEFActionConstants.DIRECT_EDIT));
		
		ChemeGraphicalKeyHandler graphicalViewerKeyHandler = new ChemeGraphicalKeyHandler(viewer);
		KeyHandler parentKeyHandler = graphicalViewerKeyHandler.setParent(keyHandler);
		viewer.setKeyHandler(parentKeyHandler);
	}
	
	protected void initializeGraphicalViewer()
	{
		GraphicalViewer viewer = getGraphicalViewer();

		if(!editorSaving)
		{
			if(viewer != null)
			{
				
				viewer.setContents(processModel);	
				/*ProcessModel child1 = new ProcessModel();
				child1.setConstraint(new Rectangle(0, 0, -1, -1));
				ContentsModel parent = new ContentsModel(child1);			
				parent.addChild(child1);
				viewer.setContents(parent);*/					
							
//				loadProperties();
			}
//			if(overviewOutlinePage != null)
//			{
//				overviewOutlinePage.setContents(processModel);
//			}
		}
	}

	@Override
	public void dispose()
	{
		getSite().getWorkbenchWindow().getPartService().removePartListener(partListener);
		partListener = null;
		
//		((IFileEditorInput)getEditorInput()).getFile().getWorkspace().removeResourceChangeListener(resourceListener);
		
		// important: always call super implementation of dispose
		super.dispose();
	}

	/**
	 * Adaptable implementation for Editor
	 */
	@Override
	public Object getAdapter(Class adapter)
	{
		// we need to handle common GEF elements we created
		if(adapter == GraphicalViewer.class || adapter == EditPartViewer.class)
		{
			return getGraphicalViewer();
		}
		else if(adapter == ZoomManager.class)
		{
			return ((ScalableFreeformRootEditPart)getGraphicalViewer().getRootEditPart()).getZoomManager();
		}
		else if(adapter == CommandStack.class)
		{
			return getCommandStack();
		}
		else if(adapter == EditDomain.class)
		{
			return getEditDomain();
		}
		else if(adapter == ActionRegistry.class)
		{
			return getActionRegistry();
		}
		else if(adapter == IPropertySheetPage.class)
		{
			return getPropertySheetPage();
		}
		else if(adapter == IContentOutlinePage.class)
		{
			return getOverviewOutlinePage();
		}

		// the super implementation handles the rest
		return super.getAdapter(adapter);
	}
	
	public ChemeFileInput _getEditorInput()
	{
		return (ChemeFileInput)super.getEditorInput();
	}
	
	/**
	 * @see org.eclipse.ui.part.EditorPart#setInput(org.eclipse.ui.IEditorInput)
	 * 
	 * Sets the input to this editor. This method simply updates the
	 * internal member variable.
	 * 
	 * <p>
	 * Unlike most of the other set methods on this class, this method does
	 * not fire a property change. Clients that call this method from a
	 * subclass must ensure that they fire an
	 * IWorkbenchPartConstants.PROP_INPUT property change after calling this
	 * method but before leaving whatever public method they are in. Clients
	 * that expose this method as public API must fire the property change
	 * within their implementation of setInput.
	 * </p>
	 * 
	 * <p>
	 * Note that firing a property change may cause listeners to immediately
	 * reach back and call methods on this editor. Care should be taken not
	 * to fire the property change until the editor has fully updated its
	 * internal state to reflect the new input.
	 * </p>
	 * 
	 * @param input
	 *            the editor input
	 */
	@Override
	protected void setInput(IEditorInput input)
	{
		if(input instanceof ChemeFileInput)
		{
			try
			{
				superSetInput(input);
				
				processModel = (ProcessModel)_getEditorInput().readFromFile();
					
//				InputStream is = _getEditorInput().getInputStream();
//				
//				if(null != is)
//				{
//					processModel = (ProcessModel)readFromInputStream(is);
//				}
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
			
			if(null == processModel)
			{
				processModel = new ProcessModel();
			}
			
			ModelEnv.getInstanceFor(processModel.getRoot()).hookModelActionFactories(getCommandStack());
		}
//		else
//		{
//			try
//			{
//				superSetInput(input);
//	
//				IFile file = ((IFileEditorInput)input).getFile();
//				setPartName(file.getName());
//				InputStream is = file.getContents(true);
//				
//				processModel = (ProcessModel)readFromInputStream(is);
//			}
//			catch(CoreException ce)
//			{
//				ce.printStackTrace();
//				processModel = new ProcessModel();
//			}
//			catch(Exception e)
//			{
//				e.printStackTrace();
//			}
//		}
	}
	
	protected void superSetInput(IEditorInput input)
	{
		//TODO:
		/*
		 * The workspace never changes for an editor.  So, removing and re-adding 
		 * the resourceListener is not necessary.  But it is being done here for the 
		 * sake of proper implementation.  Plus, the resourceListener needs to be 
		 * added to the workspace the first time around.
		 */ 
		
//		if(getEditorInput() != null)
//		{
//			IFile file = ((IFileEditorInput)getEditorInput()).getFile();
//			file.getWorkspace().removeResourceChangeListener(resourceListener);
//		}

		super.setInput(input);
		setPartName(getEditorInput().getName());

//		if(getEditorInput() != null)
//		{
//			IFile file = ((IFileEditorInput)getEditorInput()).getFile();
//			file.getWorkspace().addResourceChangeListener(resourceListener);
//			setPartName(file.getName());
//		}
	}

	/**
	 * Saves the schema model to the file
	 * 
	 * @see EditorPart#doSave
	 */
	@Override
	public void doSave(final IProgressMonitor monitor)
	{
		if(_getEditorInput().getNeedsInitialSaveAs())
		{
			doSaveAs();
			return;
		}
		
		try
		{
			editorSaving = true;
			
			SafeRunner.run(
				new SafeRunnable()
				{
					public void run() throws Exception
					{
	//					saveProperties();
//						ByteArrayOutputStream out = new ByteArrayOutputStream();
//						writeToOutputStream(out);
//						
//						_getEditorInput().getOutputStream().write(out.toByteArray());
						
						_getEditorInput().writeToFile(getProcessModel());
												
//						IFile file = ((IFileEditorInput)getEditorInput()).getFile();
//						file.setContents(new ByteArrayInputStream(out.toByteArray()), true, false, monitor);
						
						getCommandStack().markSaveLocation();
					}
				});
			editorSaving = false;
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Save as not allowed
	 */
	@Override
	public void doSaveAs()
	{
		performSaveAs();
	}

	/**
	 * Save as not allowed
	 */
	@Override
	public boolean isSaveAsAllowed()
	{
		return true;
	}
	
		
	protected boolean performSaveAs()
	{
		FileDialog fd = new FileDialog(getSite().getWorkbenchWindow().getShell(), SWT.SAVE);
		fd.setText("Save");
        fd.setFilterPath(_getEditorInput().getPath().toString());
        
        String[] filterExt = { "*.cheme" };
        fd.setFilterExtensions(filterExt);
        final  String selected = fd.open();
        
//		SaveAsDialog dialog = new SaveAsDialog(getSite().getWorkbenchWindow().getShell());
//		dialog.setOriginalFile(((IFileEditorInput)getEditorInput()).getFile());
//		dialog.open();
        
//		final IPath path = fd.getResult();

		if(null == selected)
		{
			return false;
		}

//		IWorkspace workspace = ResourcesPlugin.getWorkspace();
//		final IFile file = workspace.getRoot().getFile(path);
		
		//if(!file.exists())
//		{
			WorkspaceModifyOperation op = new WorkspaceModifyOperation()
			{
				public void execute(final IProgressMonitor monitor)
				{
//					saveProperties();
					
					try
					{
//						ByteArrayOutputStream out = new ByteArrayOutputStream();
//						writeToOutputStream(out);
//						
//						FileOutputStream fileStream = new FileOutputStream(selected);
//						fileStream.write(out.toByteArray());
//						fileStream.close();
						
						_getEditorInput().setPath(selected);
						_getEditorInput().writeToFile(getProcessModel());
						
//						file.create(new ByteArrayInputStream(out.toByteArray()), true, monitor);
					}
					catch(Exception e)
					{
						e.printStackTrace();
					}
				}
			};
			
			try
			{
				new ProgressMonitorDialog(getSite().getWorkbenchWindow().getShell()).run(false, true, op);
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
//		}

		try
		{
			superSetInput(_getEditorInput()/*new ChemeFileInput(selected)*/);
			getCommandStack().markSaveLocation();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return true;
	}
	
	protected void writeToOutputStream(OutputStream os) throws IOException
	{
		ObjectOutputStream out = new ObjectOutputStream(os);
		out.writeObject(getProcessModel());
		out.close();
	}
	
	protected Object readFromInputStream(InputStream is)
	{
		Object result = null;
		
		try
		{
			ObjectInputStream in = new ObjectInputStream(is);
			result = in.readObject();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		
		return result;
	}
	
	/**
	 * Indicates if the editor has unsaved changes.
	 * 
	 * @see EditorPart#isDirty
	 */
	@Override
	public boolean isDirty()
	{
		return getCommandStack().isDirty();
	}
	
	public void commandStackChanged(EventObject event)
	{
		firePropertyChange(IEditorPart.PROP_DIRTY);
		super.commandStackChanged(event);
	}
	
	protected void closeEditor(boolean save)
	{
		getSite().getPage().closeEditor(ProcessEditor.this, save);
	}

	/**
	 * Returns the <code>CommandStack</code> of this editor's <code>EditDomain</code>.
	 * 
	 * @return the <code>CommandStack</code>
	 */
	@Override
	public CommandStack getCommandStack()
	{
		return getEditDomain().getCommandStack();
	}
	
	protected void handleActivationChanged(Event event)
	{
		IAction copy = null;
		
		if(event.type == SWT.Deactivate)
		{
			copy = getActionRegistry().getAction(ActionFactory.COPY.getId());
		}
		
		if(getEditorSite().getActionBars().getGlobalActionHandler(ActionFactory.COPY.getId()) != copy)
		{
			getEditorSite().getActionBars().setGlobalActionHandler(ActionFactory.COPY.getId(), copy);
			getEditorSite().getActionBars().updateActionBars();
		}
	}

	/**
	 * Returns the ProcessModel model associated with the editor
	 * 
	 * @return an instance of <code>ProcessModel</code>
	 */
	public ProcessModel getProcessModel()
	{
		return processModel;
	}

	/**
	 * Creates a PaletteViewerProvider that will be used to create palettes for
	 * the view and the flyout.
	 * 
	 * @return the palette provider
	 */
	@Override
	protected PaletteViewerProvider createPaletteViewerProvider()
	{
		return new ProcessPaletteViewerProvider(getEditDomain());
	}
	
	/**
	 * Creates actions and registers them to the ActionRegistry.
	 * 
	 * Actions are common objects in the Eclipse workbench to do something when
	 * user requests are initiated through menu items, toolbar buttons or
	 * context menu items. The Graphical Editing Framework provides a set of
	 * standard actions and an infrastructure for using these actions within the
	 * Graphical Editing Framework.
	 */
	@Override
	protected void createActions()
	{
		super.createActions();
		
		addEditorAction(new SaveAction(this));

		addEditPartAction(new DeleteAction((IWorkbenchPart) this));
		addEditPartAction(new DirectEditAction((IWorkbenchPart)this));
		
//		createCutCopyPasteActions();
	}
		
	protected void addEditorAction(Action action)
	{
		addAction(action);
	}

	/**
	 * Adds an <code>EditPart</code> action to this editor.
	 * <p>
	 * <code>EditPart</code> actions are actions that depend and work on the
	 * selected <code>EditPart</code>s.
	 * 
	 * @param action the <code>EditPart</code> action
	 */
	protected void addEditPartAction(SelectionAction action)
	{
		addAction(action);
		getSelectionActions().add(action.getId());
	}

	/**
	 * Adds an action to this editor's <code>ActionRegistry</code>. (This is
	 * a helper method.)
	 * 
	 * @param action the action to add.
	 */
	protected void addAction(IAction action)
	{
		getActionRegistry().registerAction(action);
	}
	
	@Override
	protected void updateActions(List actionIds)
	{
		super.updateActions(actionIds);
		
		IActionBars actionBars = getEditorSite().getActionBars();
		actionBars.updateActionBars();
	}

	/**
	 * Returns the overview for the outline view.
	 * 
	 * @return the overview
	 */
	protected OverviewOutlinePage getOverviewOutlinePage()
	{
		if(null == overviewOutlinePage && null != getGraphicalViewer())
		{
			RootEditPart rootEditPart = getGraphicalViewer().getRootEditPart();
			if(rootEditPart instanceof ScalableRootEditPart)
			{
				overviewOutlinePage = new ChemeOverviewOutline((ScalableRootEditPart)rootEditPart);
			}
		}

		return overviewOutlinePage;
	}
	
	/**
	 * Returns the undoable <code>PropertySheetPage</code> for this editor.
	 * 
	 * @return the undoable <code>PropertySheetPage</code>
	 */
	protected PropertySheetPage getPropertySheetPage()
	{
		if(null == undoablePropertySheetPage)
		{
			undoablePropertySheetPage = new PropertySheetPage();
			undoablePropertySheetPage.setRootEntry(new UndoablePropertySheetEntry(getCommandStack()));
			//undoablePropertySheetPage.setRootEntry(GEFPlugin.createUndoablePropertySheetEntry(getCommandStack()));			
		}

		return undoablePropertySheetPage;
	}

	/**
	 * @return the preferences for the Palette Flyout
	 */
	protected FlyoutPreferences getPalettePreferences()
	{
		return new PaletteFlyoutPreferences();
	}

	/**
	 * @return the PaletteRoot to be used with the PaletteViewer
	 */
	protected PaletteRoot getPaletteRoot()
	{
		return new PaletteViewerCreator().createPaletteRoot();
	}
		
	/*--------------------------------------------------------------------------
	 * There are three refrence types for snapping, rulers/guides, a background 
	 * grid, and the geometry of figures/editparts. Each has a corresponding 
	 * action for activating the GUI aspects of these features.
	 * 
	 * ToggleGridAction: An action that toggles the grid. This action keeps 
	 * 					 the visibility and enabled  properties in sync, 
	 * 					 i.e., it toggles both at the same time. This action 
	 * 					 can handle the case where these properties are not 
	 * 					 set on the given viewer initially.
	 * 
	 * ToggleRulerVisibilityAction: An action that toggles the ruler visibility 
	 * 							   property on the given viewer. This action 
	 * 							   can handle the case where that property is 
	 * 							   not set on the viewer initially.
	 * 
	 * ToggleSnapToGeometryAction: An action that toggles the snap to geometry 
	 * 							  property on the given viewer. This action 
	 * 							  can handle the case where that property is 
	 * 							  not set on the viewer initially.
	 * 
	 * That said, I am not sure if these actions are totally connected to the 
	 * workspace interface. They may only provide menus and not action bar
	 * buttons or shortcut keys. My action bar provider may need to do a 
	 * little more work for me?
	 */
	protected void setupSnapToGeometryProperties()
	{
		getGraphicalViewer().setProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED, true);
	}
	protected void setupSnapToGeometryContributors()
	{
		
	}
	protected void setupSnapToGeometryActions()
	{
		IAction snapAction = new ToggleSnapToGeometryAction(getGraphicalViewer());
		getActionRegistry().registerAction(snapAction);
	}
	
	protected void setupGridProperties()
	{
		// Grid properties
//		getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_ENABLED, 
//				new Boolean(getLogicDiagram().isGridEnabled()));
//		// We keep grid visibility and enablement in sync
//		getGraphicalViewer().setProperty(SnapToGrid.PROPERTY_GRID_VISIBLE, new Boolean(getLogicDiagram().isGridEnabled()));
	}
	protected void setupRulerProperties()
	{
		// Ruler properties
//		LogicRuler ruler = getLogicDiagram().getRuler(PositionConstants.WEST);
//		RulerProvider provider = null;
//		if (ruler != null)
//		{
//			provider = new LogicRulerProvider(ruler);
//		}
//		getGraphicalViewer().setProperty(RulerProvider.PROPERTY_VERTICAL_RULER, provider);
//		ruler = getLogicDiagram().getRuler(PositionConstants.NORTH);
//		provider = null;
//		if (ruler != null)
//		{
//			provider = new LogicRulerProvider(ruler);
//		}
//		getGraphicalViewer().setProperty(RulerProvider.PROPERTY_HORIZONTAL_RULER, provider);
//		getGraphicalViewer().setProperty(RulerProvider.PROPERTY_RULER_VISIBILITY, new Boolean(getLogicDiagram().getRulerVisibility()));
	}
	
	protected void setupZoomContributors(ZoomManager manager)
	{
		double[] zoomLevels = new double[] { 0.25, 0.5, 0.75, 1.0, 1.5, 2.0,
				2.5, 3.0, 4.0, 5.0, 10.0, 20.0 };
		manager.setZoomLevels(zoomLevels);
		
		ArrayList zoomContributions = new ArrayList();
		zoomContributions.add(ZoomManager.FIT_ALL);
		zoomContributions.add(ZoomManager.FIT_HEIGHT);
		zoomContributions.add(ZoomManager.FIT_WIDTH);
		manager.setZoomLevelContributions(zoomContributions);
	}
	protected void setupZoomProperties(ZoomManager manager)
	{
		if (manager != null && processModel != null)
		{
			manager.setZoom(processModel.getZoom());
		}
		else
		{
			manager.setZoom(1.0);
		}
		
		// Scroll-wheel Zoom
		getGraphicalViewer().setProperty(MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1), MouseWheelZoomHandler.SINGLETON);
	}
	protected void setupZoomActions(ZoomManager manager)
	{
		IAction action = new ZoomInAction(manager);
		getActionRegistry().registerAction(action);

		action = new ZoomOutAction(manager);
		getActionRegistry().registerAction(action);
	}
}
